#ifndef LEXEM_TABLE_H
#define LEXEM_TABLE_H

#include <string>
#include <map>
#include "types.h"

enum TokenType { NoType, Operation, Keyword, Appropriation, Identificator, Add, Sub = 7, Literal, Multiplication, Separator = 16,
                 Related, Loop = 19, Conditional = 21, Function = 23, Division = 25, ModuleDivision = 27 }; // тип токена !!!Виправити!!
enum IdentificatorType {NoIdentType, Array = 1, Bool, String = 4, Symbol = 6, Struct = 8 }; // тип ідентифікатора

/* Токен */
struct TokenEntry
{
    TokenEntry(std::string t, TokenType ty)
        : token_name(t), token_type(ty), real_name(t) {}
    TokenEntry(std::string t)
        : token_name(t), real_name(t), token_type(NoType) {}
    TokenEntry(std::string t, std::string r_n, TokenType ty)
        : token_name(t), token_type(ty), real_name(r_n) {}
    TokenEntry(char t, TokenType ty)
        : token_type(ty)
    {
        token_name = t;
        real_name = t;
    }
    TokenEntry()
        : token_type(NoType) {}
    std::string token_name; // ім'я токена. Якщо токен - змінна - token == "varname". Усі змінні в граматиці позначаються через "varname"
    std::string real_name; // якщо токен змінна - real_name містить справжнє ім'я змінної. В інших випадках real_name == token_name
    TokenType token_type;
};

/* Описання елементів структури */
class StructEntry
{

};

/* Ідентифікатор */
class IdentificatorEntry
{
    bool is_declaration_var; // флаг показує чи була об'явлена дана змінна. Використовується на етапі синтаксичного аналіз
    bool is_function_parametr; // флаг показує, що дана змінна являється параметром ф-ції
    std::string name_var; // ім'я змінної
    std::string value_var; // присвоєне значення
    sint offset_stack; // зміщення відносно регістра %ebp: -offset_stack(%ebp) - адрес локальної змінної
    IdentificatorType identificator_type; // тип ідентифікатора: Symbol, Array, Struct
    sint array_size; // кількість елементів масиву. Використовується, якщо (IdentificatorType & Array) == 1
    ushort type_size; // розмір типу
    StructEntry* struct_entry; // вказівник на описання елементів структури. Використовується, якщо (IdentificatorType & Struct) == 1
public:
    IdentificatorEntry(std::string i)
        : name_var(i), struct_entry(0), type_size(0), is_declaration_var(true), identificator_type(NoIdentType),
          is_function_parametr(false) {}
    IdentificatorEntry()
        : struct_entry(0), type_size(0), is_declaration_var(true), identificator_type(NoIdentType), is_function_parametr(false) {}
    IdentificatorEntry(std::string name, IdentificatorType type, sint t_size)
        : name_var(name), identificator_type(type), type_size(t_size), is_function_parametr(true), struct_entry(0),
          is_declaration_var(true) {} // цей конструктор потрібно визивати для встроєних ф-цій компілятора (stdout, stdin)
    ~IdentificatorEntry() { delete struct_entry; }
    void setIsFunctionParametr() { is_function_parametr = true; }
    bool isDeclaratedVar() const { return is_declaration_var; }
    bool isFunctionParametr() const { return is_function_parametr; }
    void setTypeVar(IdentificatorType type)
    {
        is_declaration_var = false; // дана змінна об'явлена. Використання необ'явленої змінної - синтаксична помилка
        identificator_type = type;
    }
    void setStructEntry(StructEntry* struc) { struct_entry = struc; }
    void setArraySize(sint size) { array_size = size; }
    void setTypeSize(ushort ssize) { type_size = ssize; }
    void setNameVar(std::string name) { name_var = name; }
    void setValueVar(std::string value) { value_var = value; }
    void setOffsetStack(sint offset) { offset_stack = offset; }
    sint getOffsetStack() const { return offset_stack; }
    sint getArraySize() const { return array_size; }
    sint getTypeSize() const { return type_size; }
    std::string getNameVar() const { return name_var; }
    std::string getValueVar() const { return value_var; }
    IdentificatorType getTypeVar() const { return identificator_type; }
    StructEntry* getStructEntry() { return struct_entry; }
};

/* ключові слова мови програмування */
class LanguageKeyWords
{
    std::map<std::string, TokenEntry> keywords_table; // таблиця ключових слів
public:
    LanguageKeyWords()
    {
        std::string keyword_operations[] = {"for", "do", "if", "else", "break", "case", "switch", "continue", "/",
                                            "!", "&&", "||", "&", "|"};
        for (int i = 0; i < sizeof(keyword_operations) / sizeof(std::string); i++)
            keywords_table[keyword_operations[i]] = TokenEntry(keyword_operations[i], Operation); // виділяємо лексеми - операції
        keywords_table[">"] = TokenEntry(">", Related);
        keywords_table["<"] = TokenEntry("<", Related);
        keywords_table["=="] = TokenEntry("==", Related);
        keywords_table["!="] = TokenEntry("!=", Related);
        keywords_table[">="] = TokenEntry(">=", Related);
        keywords_table["<="] = TokenEntry("<=", Related);
        keywords_table["="] = TokenEntry("=", Appropriation);
        keywords_table["*"] = TokenEntry("*", Multiplication);
        keywords_table["/"] = TokenEntry("/", Division);
        keywords_table["%"] = TokenEntry("%", ModuleDivision);
        keywords_table["+"] = TokenEntry("+", Add);
        keywords_table["-"] = TokenEntry("-", Sub);
        keywords_table["while"] = TokenEntry("while", Loop);
        keywords_table["if"] = TokenEntry("if", Conditional);
        keywords_table["else"] = TokenEntry("else", Conditional);
        keywords_table["funcname"] = TokenEntry("funcname", Function); // funcname не являється ключовим словом, але ф-ція є операцією
        std::string keyword[] = {"symbol", "bool", "string"}; // виділяємо лексеми - інші ключові слова
        for (int i = 0; i < sizeof(keyword) / sizeof(std::string); i++) keywords_table[keyword[i]] = TokenEntry(keyword[i], Keyword);
    }
    TokenEntry operator[](std::string s) { return keywords_table[s]; }
};

#endif // LEXEM_TABLE_H
